package club.kynb.mall.application.flow.order;

import club.kynb.mall.application.context.OrderPreviewContext;
import club.kynb.mall.application.model.model.dto.UserReceiveCouponDTO;
import club.kynb.mall.application.service.CouponApplicationService;
import club.kynb.mall.product.constant.CouponSpuRangeEnum;
import club.kynb.mall.product.constant.UserCouponStatusEnum;
import club.kynb.mall.product.dto.ProductSpuDetailDTO;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.pizza.pattern.filter.Filter;
import org.pizza.pattern.filter.FilterInvoker;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author kynb_club@163.com
 * @since 2021/7/5 9:02 下午
 */
@Slf4j
@Component
@AllArgsConstructor
public class RecommendUserCouponFilter implements Filter<OrderPreviewContext> {
    private final CouponApplicationService couponApplicationService;

    @Override
    public void doFilter(OrderPreviewContext orderPreviewContext, FilterInvoker<OrderPreviewContext> filterInvoker) {
        log.info("推荐优惠券过滤器 start");
        final List<UserReceiveCouponDTO> userCouponList = couponApplicationService.userCouponList(-1);
        //过滤未使用and未过期
        final List<UserReceiveCouponDTO> canUseList = userCouponList.stream()
                .filter(userReceiveCouponDTO -> userReceiveCouponDTO.getStatus().equals(UserCouponStatusEnum.UN_USE.getStatus()))
                .filter(userReceiveCouponDTO -> DateUtil.isIn(new Date(), userReceiveCouponDTO.getEffectiveTime(), userReceiveCouponDTO.getInvalidTime()))
                .collect(Collectors.toList());
        final List<ProductSpuDetailDTO> filteredSpuList = orderPreviewContext.getFilteredSpuList();
        //找到当前订单可以使用的优惠券
        List<UserReceiveCouponDTO> orderCanUseList = this.findCanUse(orderPreviewContext, canUseList, filteredSpuList);
        orderPreviewContext.getOrderPreviewResultVO().setUserCouponList(orderCanUseList);
        //最优选择
        final Optional<UserReceiveCouponDTO> max = orderCanUseList.stream().max(Comparator.comparing(userReceiveCouponDTO -> new BigDecimal(userReceiveCouponDTO.getCouponValue())));
        max.ifPresent(couponDTO -> orderPreviewContext.getOrderPreviewResultVO().setRecommendCoupon(couponDTO));
        log.info("推荐优惠券过滤器 end");
        filterInvoker.invoke(orderPreviewContext);
    }

    private List<UserReceiveCouponDTO> findCanUse(OrderPreviewContext orderPreviewContext, List<UserReceiveCouponDTO> userCouponList, List<ProductSpuDetailDTO> filteredSpuList) {
        return userCouponList.stream()
                .filter(userReceiveCouponDTO -> orderCanUse(orderPreviewContext, userReceiveCouponDTO, filteredSpuList))
                .collect(Collectors.toList());
    }

    private boolean orderCanUse(OrderPreviewContext orderPreviewContext, UserReceiveCouponDTO userReceiveCouponDTO, List<ProductSpuDetailDTO> filteredSpuList) {
        //全场通用
        if (userReceiveCouponDTO.getSpuRange().equals(CouponSpuRangeEnum.ALL.getType())) {
            final BigDecimal totalAmount = orderPreviewContext.calculateTotalAmount(filteredSpuList, orderPreviewContext.getCheckedList());
            return NumberUtil.isGreaterOrEqual(totalAmount, new BigDecimal(userReceiveCouponDTO.getThresholdAmount()));
        }
        //指定商品
        final String spuIds = userReceiveCouponDTO.getSpuIds();
        final List<Long> spuIdList = JSONUtil.toList(JSONUtil.parseArray(spuIds), Long.class);
        if (CollectionUtil.isEmpty(spuIdList)) {
            log.error("优惠券配置有误，优惠券ID：{} 类型为指定商品，但未spuId列表为空",userReceiveCouponDTO.getId());
            return false;
        }
        //过滤满足优惠券要求的商品
        final List<ProductSpuDetailDTO> spuDTOList = filteredSpuList
                .stream()
                .filter(productSpuDTO -> spuIdList.contains(Long.valueOf(productSpuDTO.getId())))
                .collect(Collectors.toList());
        final BigDecimal totalAmount = orderPreviewContext.calculateTotalAmount(spuDTOList, orderPreviewContext.getCheckedList());
        return NumberUtil.isGreaterOrEqual(totalAmount, new BigDecimal(userReceiveCouponDTO.getThresholdAmount()));
    }
}
